package online.hxstart.code.concurrent.chapter08;

import java.util.ArrayDeque;
import java.util.Queue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import online.hxstart.code.concurrent.chapter08.DenyPolicy.DiscardDenyPolicy;

/**
 * @author huxing
 * @date 2019/7/11 星期四 14:33
 * @describe
 */
public class BasicThreadPool extends Thread implements ThreadPool {

  /**
   * 初始化线程数量
   */
  private final int initSize;

  /**
   * 线程池最大线程数量
   */
  private final int maxSize;

  /**
   * 线程池核心线程数量
   */
  private final int coreSize;

  /**
   * 当前活跃线程数量
   */
  private int activeCount;

  private final ThreadFactory threadFactory;

  private final RunnableQueue runnableQueue;

  private volatile boolean isShutDown = false;

  private final Queue<ThreadTask> threadQuery = new ArrayDeque<>();

  private static final DenyPolicy DEFAULT_DENY_POLICY = new DiscardDenyPolicy();

  private static final ThreadFactory DEFAULT_THREAD_FACTORY = new DefaultThreadFactory();

  /**
   * 存活时间
   */
  private final long keepAliveTime;

  private final TimeUnit timeUnit;

  public BasicThreadPool(
      int initSize,
      int maxSize,
      int coreSize,
      int queueSize,
      ThreadFactory threadFactory,
      DenyPolicy denyPolicy,
      long keepAliveTime,
      TimeUnit timeUnit) {
    this.initSize = initSize;
    this.maxSize = maxSize;
    this.coreSize = coreSize;
    this.threadFactory = threadFactory;
    this.runnableQueue = new LinkedRunnableQueue(queueSize, denyPolicy, this);
    this.keepAliveTime = keepAliveTime;
    this.timeUnit = timeUnit;
    this.init();
  }

  public BasicThreadPool(int initSize, int maxSize, int coreSize, int querySize) {
    this(
        initSize,
        maxSize,
        coreSize,
        querySize,
        DEFAULT_THREAD_FACTORY,
        DEFAULT_DENY_POLICY,
        10,
        TimeUnit.SECONDS);
  }

  private void init() {
    start();
    for (int i = 0; i < initSize; i++) {
      newThread();
    }
  }

  private void newThread() {
    //创建任务线程并启动
    InternalTask internalTask = new InternalTask(runnableQueue);
    Thread thread = this.threadFactory.createThread(internalTask);
    ThreadTask threadTask = new ThreadTask(thread, internalTask);
    threadQuery.offer(threadTask);
    this.activeCount++;
    thread.start();
  }

  private void removeThread() {
    ThreadTask threadTask = threadQuery.remove();
    threadTask.internalTask.stop();
    this.activeCount--;
  }


  @Override
  public void run() {
    while (!isShutDown && !isInterrupted()) {
      try {
        timeUnit.sleep(keepAliveTime);
      } catch (InterruptedException e) {
        isShutDown = true;
        break;
      }
      synchronized (this) {
        if (isShutDown) {
          break;
        }
        if (runnableQueue.size() > 0 && activeCount < maxSize) {

          for (int i = coreSize; i < maxSize; i++) {
            newThread();
          }
        }
        if (runnableQueue.size() == 0 && activeCount > coreSize) {
          for (int i = coreSize; i < activeCount; i++) {
            removeThread();
          }
        }
      }
    }
  }

  @Override
  public void execute(Runnable runnable) {
    if (this.isShutDown) {
      throw new IllegalStateException("The thread pool is destroy");
    } else {
      this.runnableQueue.offer(runnable);
    }
  }

  @Override
  public void shutdown() {
    synchronized (this) {
      if (isShutDown) {
        return;
      }
      isShutDown = true;
      threadQuery.forEach(threadTask -> {
        threadTask.internalTask.stop();
        threadTask.thread.interrupt();
      });
      this.interrupt();
    }
  }

  @Override
  public int getInitSize() {
    if (isShutDown) {
      throw new IllegalStateException("The thread pool is destroy");
    }
    return this.initSize;
  }

  @Override
  public int getMaxSize() {
    if (isShutDown) {
      throw new IllegalStateException("The thread pool is destroy");
    }
    return this.maxSize;
  }

  @Override
  public int getQueueSize() {
    if (isShutDown) {
      throw new IllegalStateException("The thread pool is destroy");
    }
    return this.runnableQueue.size();
  }

  @Override
  public int getCoreSize() {
    if (isShutDown) {
      throw new IllegalStateException("The thread pool is destroy");
    }
    return this.coreSize;
  }

  @Override
  public int getActiveCount() {
    if (isShutDown) {
      throw new IllegalStateException("The thread pool is destroy");
    }
    return this.activeCount;
  }

  @Override
  public boolean isShutdown() {
    return this.isShutDown;
  }

  private static class ThreadTask {

    Thread thread;
    InternalTask internalTask;

    ThreadTask(Thread thread, InternalTask internalTask) {
      this.thread = thread;
      this.internalTask = internalTask;
    }
  }

  private static class DefaultThreadFactory implements ThreadFactory {

    private static final AtomicInteger GROUP_COUNTER = new AtomicInteger(1);

    private static final ThreadGroup GROUP = new ThreadGroup(
        "MyThreadPool-" + GROUP_COUNTER.getAndIncrement());
    private static final AtomicInteger COUNTER = new AtomicInteger(0);

    @Override
    public Thread createThread(Runnable runnable) {
      return new Thread(GROUP, runnable, "thread-pool-" + COUNTER.getAndIncrement());
    }
  }
}
